Chapter 13 - Foreach
You can create resources from a list of strings (called a map)
You cannot use count with foreach.
Intro to each.key and each.value
Demo
THe part that confused me the most about this was that the for_each block ended, and then the each.key or each.value was independent. The "myrg" part in the resource identifier also confused me.
I would think something like
foreach (value in values)
{
resource azurerm_rg "myrg-value"
{
name = value
value = value
}
}
Terraform's foreach does it this way:
# Build Azure Resource Group
resource "azurerm_resource_group" "myrg" {
for_each = {
dc1apps = "eastus"
dc2apps = "eastus2"
dc3apps = "westus"
}
name = "${each.key}-rg"
location = each.value
}
How this looks during a plan and apply is this:
Toset
What is a set in terraform?
- must be of the same type - strings, numbers, but not both.
- cannot contain duplicate values
Terraform console
Paste these into the terraform console:
# Terraform console
terraform console
# All Strings to Strings
toset(["solomon", "jordan", "liz"])
# Mixed Type (Strings and Numbers) - Converted to Strings
toset(["solo", "liz", 123, 456])
# Removes duplicates (Set collections are unordered and cannot contain duplicate values,)
toset(["z", "k", "r", "a", "k"])
# Also arranges in the order (The order provided will be gone) - In short set collections are unordered
toset([4, 100, 20, 11, 21, 7, 6, 4, 100])
It places these in alphabetical order:
It changed the numbers to strings:
Or numerical order:
As terraform code:
resource "azurerm_resource_group" "myrg" {
for_each = toset([ "eastus", "eastus2", "westus" ])
name = "myrg-${each.value}"
location = each.key
}
/*
each.value is the same as each.key in this case
*/
Something like this would work:
resource "azurerm_resource_group" "myrg" {
for_each = toset([ "dev", "qa", "uat", "perf", "prod" ])
name = "appaa123-${each.value}"
location = "centralus"
}
Foreach Chaining
You created one set of RGs with a foreach, what happens when you want other resources to follow this same foreach? Foreach Chaining :)
resource "azurerm_resource_group" "myrg" {
for_each = toset([ "dev", "dev2", "dev3", "qa", "uat", "stage", "prod" ])
name = "appaa123-${each.value}"
location = "centralus"
}
# Create Virtual Network
resource "azurerm_virtual_network" "myvnet" {
for_each = azurerm_resource_group.myrg
name = "appaa123-vnet-${each.key}"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.myrg[each.key].location
resource_group_name = azurerm_resource_group.myrg[each.key].name
}
# Create Subnet
resource "azurerm_subnet" "mysubnet" {
for_each = azurerm_virtual_network.myvnet
name = "appaa123-sn-${each.key}"
resource_group_name = azurerm_resource_group.myrg[each.key].name
virtual_network_name = azurerm_virtual_network.myvnet[each.key].name
address_prefixes = ["10.0.2.0/24"]
}
# Create Azure Public IP Address
resource "azurerm_public_ip" "mypublicip" {
for_each = azurerm_subnet.mysubnet
name = "appaa123-puip-${each.key}"
resource_group_name = azurerm_resource_group.myrg[each.key].name
location = azurerm_resource_group.myrg[each.key].location
allocation_method = "Static"
domain_name_label = "app1-${each.key}-${random_string.myrandom.id}"
}
# # Create Network Interface
resource "azurerm_network_interface" "myvmnic" {
for_each = azurerm_public_ip.mypublicip
name = "appaa123-nic-${each.key}"
location = azurerm_resource_group.myrg[each.key].location
resource_group_name = azurerm_resource_group.myrg[each.key].name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.mysubnet[each.key].id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.mypublicip[each.key].id
}
}
# # Resource: Azure Linux Virtual Machine
resource "azurerm_linux_virtual_machine" "mylinuxvm" {
#for_each = toset(["vm1", "vm2"])
for_each = azurerm_network_interface.myvmnic #for_each chaining
name = "appaa123-vm-${each.key}"
computer_name = "appaa123-vm-${each.key}" # Hostname of the VM
resource_group_name = azurerm_resource_group.myrg[each.key].name
location = azurerm_resource_group.myrg[each.key].location
size = "Standard_DS1_v2"
admin_username = "azureuser"
network_interface_ids = [azurerm_network_interface.myvmnic[each.key].id]
admin_ssh_key {
username = "azureuser"
public_key = file("${path.module}/ssh-keys/terraform-azure.pub")
}
os_disk {
name = "osdisk${each.key}"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
#disk_size_gb = 20
}
source_image_reference {
publisher = "RedHat"
offer = "RHEL"
sku = "83-gen2"
version = "latest"
}
custom_data = filebase64("${path.module}/app-scripts/app1-cloud-init.txt")
}